﻿using System;
using System.Net;
using System.Windows;
using Microsoft.Phone.Controls;
using System.Diagnostics;
using Newtonsoft.Json;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Tasks;
using Windows.Phone.Speech.Synthesis;
using System.Windows.Media.Imaging;
using Windows.Phone.Speech.Recognition;
using OpenThesaurus.Resources;
using System.Collections.Generic; // necessary to access i18n resources
using System.Linq;
using System.IO.IsolatedStorage;
using System.Globalization;
using System.Windows.Navigation;
using Windows.Phone.UI.Input;
using System.ComponentModel;

namespace OpenThesaurus
{
    public partial class Search : PhoneApplicationPage
    {
        /// <summary>
        /// The progress indicator that appears when contacting the openthesaurus.de backend.
        /// </summary>
        private ProgressIndicator progressIndicator = null;

        /// <summary>
        /// Get the current app context.
        /// </summary>
        private App currentApp = App.Current as App;

        /// <summary>
        /// Indicates if the selected background theme is light or dark.
        /// </summary>
        private static bool lightThemeActive;

        /// <summary>
        /// A list used to store the list of search terms to be able to go back to previous search terms.
        /// </summary>
        internal static List<string> searchTermHistory = new List<string>();

        /// <summary>
        /// The options that can be configured for search.
        /// </summary>
        private Options searchOptions;
        
        public Search()
        {
            InitializeComponent();
            initializeAppBarMenuItems();
            updateSubmitButtonStatus();
            initializeContentGridColumns();
            checkForLightBackgroundTheme();
            searchOptions = new Options();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            if ((e.NavigationMode == NavigationMode.New) || (e.NavigationMode == NavigationMode.Refresh))
            {
                string recognizedText = null; // What did the user say? e.g. "Thesaurus, Find Hallo"
                NavigationContext.QueryString.TryGetValue("reco", out recognizedText);

                string voiceCommandName = null; // Which command was recognized in the VoiceCommands.xml file? e.g. "Suche"
                NavigationContext.QueryString.TryGetValue("voiceCommandName", out voiceCommandName); 

                string searchTerms = null; // What did the user say, for named phrase topic or list "slots"? e.g. Windows Phone Voice Commands
                NavigationContext.QueryString.TryGetValue("dictatedSearchTerms", out searchTerms);

                if ((voiceCommandName == "ShowSynonyms") && (!string.IsNullOrEmpty(searchTerms)))
                {
                    // delete the navigation history completely to avoid multiple-back steps when using the search term history feature.
                    while (NavigationService.RemoveBackEntry() != null) ;

                    txtSearchInput.Text = searchTerms;
                    getTranslationFromBackend(searchTerms);
                }
            }
        }

        /// <summary>
        /// Ask the user if he really wants to exit the app.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnBackKeyPress(CancelEventArgs e)
        {
            if (!NavigationService.CanGoBack)
            {
                MessageBoxResult result = MessageBox.Show(AppResources.ExitQuestion, AppResources.Question, MessageBoxButton.OKCancel);

                if (result.Equals(MessageBoxResult.Cancel))
                {
                    e.Cancel = true;
                }
            }
        }

        /// <summary>
        /// Checks if the light background theme is active
        /// </summary>
        private void checkForLightBackgroundTheme()
        {
            Search.lightThemeActive = (Visibility) Resources["PhoneLightThemeVisibility"] == Visibility.Visible;
        }

        /// <summary>
        /// Initialize the app bar menu items with i18n support.
        /// </summary>
        private void initializeAppBarMenuItems()
        {
            #region Create & configure the app's menu item controls

            ApplicationBarMenuItem appBarMenuItem_history = new ApplicationBarMenuItem(AppResources.SearchTermHistory);
            appBarMenuItem_history.Click += new EventHandler(appBarHistory_Click);

            ApplicationBarMenuItem appBarMenuItem_options = new ApplicationBarMenuItem(AppResources.TitleOptions);
            appBarMenuItem_options.Click += new EventHandler(appBarOptions_Click);

            ApplicationBarMenuItem appBarMenuItem_contactForm = new ApplicationBarMenuItem(AppResources.ContactForm);
            appBarMenuItem_contactForm.Click += new EventHandler(appBarContactForm_Click);
            
            ApplicationBarMenuItem appBarMenuItem_rate = new ApplicationBarMenuItem(AppResources.Rate);
            appBarMenuItem_rate.Click += new EventHandler(appBarRating_Click);

            ApplicationBarMenuItem appBarMenuItem_info = new ApplicationBarMenuItem(AppResources.Info);
            appBarMenuItem_info.Click += new EventHandler(appBarInfo_Click);

            // add the menu items to the app bar
            ApplicationBar.MenuItems.Add(appBarMenuItem_history);
            ApplicationBar.MenuItems.Add(appBarMenuItem_options);
            ApplicationBar.MenuItems.Add(appBarMenuItem_contactForm);
            ApplicationBar.MenuItems.Add(appBarMenuItem_rate);
            ApplicationBar.MenuItems.Add(appBarMenuItem_info);
            #endregion
        }

        /// <summary>
        /// Prepare the Speech to Text button if the current culture is supported.
        /// </summary>
        private void prepareStt()
        {
            // enable STT if the current culture is German
            if (CultureInfo.CurrentUICulture.Name == "de-DE")
            {
                // show the STT icon
                btnStt.Visibility = Visibility.Visible;

                // Restrict the text input field to the available space on the right
                Grid.SetColumn(txtSearchInput, 1);
                Grid.SetColumnSpan(txtSearchInput, 1);

                showWhiteOrDarkSttIcon();
            }
        }

        /// <summary>
        /// Initializes the content grid columns
        /// </summary>
        private void initializeContentGridColumns()
        {
            // define two columns: the first for the term button and the second one for the tts button
            ColumnDefinition firstColumn = new ColumnDefinition();
            ColumnDefinition secondColumn = new ColumnDefinition();
            firstColumn.Width = new GridLength(80, GridUnitType.Star);
            secondColumn.Width = new GridLength(20, GridUnitType.Star);

            // add the column definitions to the content grid
            gridContent.ColumnDefinitions.Add(firstColumn);
            gridContent.ColumnDefinitions.Add(secondColumn);
        }

        /// <summary>
        /// Update the status of the submit button after each text change.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSearchInput_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            updateSubmitButtonStatus();
        }

        /// <summary>
        /// Disable the submit button on text input that is shorter than two characters to avoid useless requests.
        /// </summary>
        private void updateSubmitButtonStatus()
        {
            if (txtSearchInput.Text.Length < 2)
            {
                disableSubmitButton();
            }
            else
            {
                enableSubmitButton();
            }
        }

        /// <summary>
        /// Enables the submit button and adjusts the opacity.
        /// </summary>
        private void enableSubmitButton()
        {
            btnSubmit.IsEnabled = true;
            btnSubmit.Opacity = 1;
        }

        /// <summary>
        /// Disables the submit button and adjusts the opacity.
        /// </summary>
        private void disableSubmitButton()
        {
            btnSubmit.IsEnabled = false;
            btnSubmit.Opacity = 0.33;
        }

        /// <summary>
        ///  Sends the query to the backend API and registers two asynchronuous methods to receive the response.
        /// </summary>
        /// <param name="searchTerm">The search term</param>
        private void getTranslationFromBackend(string searchTerm)   
        {
            try
            {
                string requestString = App.API_URL + Uri.EscapeUriString(searchTerm.Trim());

                // get the current app context to get the required values from the Options page
                //App currentApp = Application.Current as App;

                // add the optional request parameter to get similar words if the option is selected on the Options page
                if (IsolatedStorageSettings.ApplicationSettings.Contains(Options.OPTION_SIMILAR_RESULTS))
                {
                    if (IsolatedStorageSettings.ApplicationSettings[Options.OPTION_SIMILAR_RESULTS] != null 
                        && IsolatedStorageSettings.ApplicationSettings[Options.OPTION_SIMILAR_RESULTS].ToString().ToLower() == "true")
                    {
                        requestString += App.API_PARAMETER_SIMILARWORDS;
                    }
                }

                // add the optional request parameter to get substrings if the option is selected on the Options page
                if (IsolatedStorageSettings.ApplicationSettings.Contains(Options.OPTION_SUBSTRING_RESULTS))
                {
                    if (IsolatedStorageSettings.ApplicationSettings[Options.OPTION_SUBSTRING_RESULTS] != null
                        && IsolatedStorageSettings.ApplicationSettings[Options.OPTION_SUBSTRING_RESULTS].ToString().ToLower() == "true")
                    {
                        requestString += App.API_PARAMETER_SUBSTRING;
                    }
                }

                Debug.WriteLine("GET request string: " + requestString);

                // Configure the progress indicator
                progressIndicator = new ProgressIndicator()
                {
                    IsVisible = true,
                    IsIndeterminate = true,
                    Text = AppResources.ProgressIndicatorText
                };

                // Show the network activity to the user
                SystemTray.SetProgressIndicator(this, progressIndicator);

                #region Create & configure the web client
                WebClient webClient = new WebClient();
                // identify as a unbranded Windows Phone 8 smartphone
                webClient.Headers[HttpRequestHeader.UserAgent] = "Mozilla/5.0 (compatible; MSIE; Windows Phone; Trident; IEMobile; ARM; Touch;)";
                webClient.DownloadStringAsync(new Uri(requestString));

                webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(DownloadStringCompleted);
                webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
                #endregion
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error while building and escaping the GET request and start the request!");
                Debug.WriteLine(ex.Message);
                MessageBox.Show(AppResources.MessageNetworkError, AppResources.MessageBoxTitleInfo, MessageBoxButton.OK);
            }
        }

        /// <summary>
        /// During the download the submit button gets disabled to avoid double trigger.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            // disable the submit button after click to avoid double trigger
            disableSubmitButton();

            Debug.WriteLine(e.BytesReceived.ToString() + " bytes received.");
        }

        /// <summary>
        /// Handles the downloaded data after the backend information is complete.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            progressIndicator.IsVisible = false;

            // check if an error occured during the network request
            if (e.Error == null && !e.Cancelled)
            {
                Debug.WriteLine("Response from openthesaurus.de API:" + e.Result);
                
                // remove all existing child controls
                gridContent.Children.Clear();

                // row counter for grid row counter
                // located here to allow use in catch block below
                int rowCount = 0;

                // the offset is required since non-term controls like text hints could be available, too
                int rowOffset = 0;

                try
                {
                    // scroll to the top after each new request
                    scrollViewerContent.ScrollToVerticalOffset(0);

                    // Convert the response from a string response to a Search object
                    OpenThesaurus.DAOs.Search searchResponse = JsonConvert.DeserializeObject<OpenThesaurus.DAOs.Search>(e.Result);

                    // compatibility: check if the API version change in the meanwhile
                    if (searchResponse.metaData.apiVersion != App.API_VERSION)
                    {
                        Debug.WriteLine("!! WARNING !! The API version changed !");
                        MessageBox.Show(AppResources.MessageApiChanged, AppResources.MessageBoxTitleWarning, MessageBoxButton.OK);
                    }

                    // Get the current Phone Accent Color
                    Color currentAccentColorHex = (Color)Application.Current.Resources["PhoneAccentColor"];
                    SolidColorBrush borderBrush = new SolidColorBrush(currentAccentColorHex);

                    // In case no matching results are returned by the openthesaurus.de API
                    if (searchResponse.synsets.Count > 0)
                    {
                        // Create new control which will show the received term
                        Button btnTerm = null;

                        // Create a new border control to separate the different categories
                        //Border borderSeparator = null;

                        // Create a new border control to enable rounded corners to the returned terms
                        Border borderTerm = null;

                        // button for text-to-speech
                        Button btnTts = null;

                        // Image for text-to-speech icon
                        Image imageTts = null;
                        // read a bitmap with the icon source

                        // set the correct tts icon dependent from the active background (light or dark) 
                        BitmapImage imageTtsSource = null;
                        if (Search.lightThemeActive)
                        {
                            imageTtsSource = new BitmapImage(new Uri(@"Assets/transport.play.black.png", UriKind.RelativeOrAbsolute));
                        }
                        else
                        {
                            imageTtsSource = new BitmapImage(new Uri(@"Assets/transport.play.png", UriKind.RelativeOrAbsolute));
                        }

                        // Iterate over all contained synonym sets
                        foreach (OpenThesaurus.DAOs.Synset synonymSet in searchResponse.synsets)
                        {
                            // Iterate over all contained terms
                            foreach (OpenThesaurus.DAOs.Term terms in synonymSet.terms)
                            {
                                #region Create & configure the border control
                                // parent control which will contain the term button
                                borderTerm = new Border();
                                borderTerm.CornerRadius = new CornerRadius(25);
                                borderTerm.BorderBrush = borderBrush; // assign the Phone Accent Color to the term border
                                borderTerm.BorderThickness = new Thickness(3, 3, 3, 3);
                                borderTerm.Margin = new Thickness(0, 0, 0, 10); // margin between each single control in list
                                #endregion

                                // assign the icon source to the image control
                                imageTts = new Image();
                                imageTts.Source = imageTtsSource;

                                #region Create & configure the text-to-speech button
                                // initialize the tts button
                                btnTts = new Button();
                                btnTts.Content = imageTts; // add the text-to-speech icon to the tts button
                                btnTts.Name = "ttsButtonInRow_" + rowCount; // necessary to identify it later in the grid
                                btnTts.BorderThickness = new Thickness(1, 1, 1, 1); // remove borders
                                btnTts.Click += new RoutedEventHandler(OnTtsButtonClicked);
                                btnTts.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                                btnTts.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
                                btnTts.Margin = new Thickness(0, 0, 0, 10); // same margin between each single control in list as the border term control
                                #endregion

                                #region check if a level attribute is available and add the level text
                                Debug.WriteLine("Value of \"level\" attribute: ==>" + terms.level + "<==");
                                string leveltext = "";
                                if (terms.level != null)
                                {
                                    leveltext = "\n[" + terms.level + "]";
                                }
                                #endregion


                                #region Use a TextBlock control for text to support word wrap for long words (without it, long words are simply cut)
                                TextBlock txtTermContent = new TextBlock();
                                txtTermContent.Text = terms.term + leveltext;
                                txtTermContent.TextAlignment = TextAlignment.Center;
                                txtTermContent.TextWrapping = TextWrapping.Wrap;
                                txtTermContent.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                                txtTermContent.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                                #endregion

                                #region Create & configure one button for each single term
                                btnTerm = new Button();
                                btnTerm.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                                btnTerm.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                                btnTerm.BorderThickness = new Thickness(0, 0, 0, 0); // remove button border - will be replaced by the rounded border of the parent control
                                btnTerm.Click += new RoutedEventHandler(OnTermButtonClicked);
                                #endregion

                                // assign the TextBlock control to the term button
                                btnTerm.Content = txtTermContent;

                                // put the button into the border control (necessary to have rounded corners)
                                borderTerm.Child = btnTerm;

                                #region Align the border term control in the content grid
                                RowDefinition newGridRow_Term = new RowDefinition();
                                newGridRow_Term.Height = new GridLength(0, GridUnitType.Auto);
                                gridContent.RowDefinitions.Add(newGridRow_Term);

                                gridContent.Children.Add(borderTerm);
                                borderTerm.SetValue(Grid.RowProperty, rowCount);
                                borderTerm.SetValue(Grid.ColumnProperty, 0);

                                // align the text-to-speech button in the content grid
                                gridContent.Children.Add(btnTts);
                                btnTts.SetValue(Grid.RowProperty, rowCount);
                                btnTts.SetValue(Grid.ColumnProperty, 1);
                                #endregion

                                // add every new control (border with textx box inside) to the content stack panel
                                //stackPanelContent.Children.Add(borderTerm);

                                //stackPanelContent.Children.Add(btnTts);

                                // increment the row count value by one after each iteration. Necessary to set a valid row id in the content grid.
                                rowCount++;
                            }

                        }
                    }
                    else
                    {
                        #region Create a text block with a message to inform the user that no direct results were found
                        TextBlock txtNoDirectSearchResults = new TextBlock();
                        txtNoDirectSearchResults.Text = AppResources.MessageNoDirectResultsFound;
                        txtNoDirectSearchResults.TextAlignment = TextAlignment.Center;
                        txtNoDirectSearchResults.VerticalAlignment = System.Windows.VerticalAlignment.Top;
                        txtNoDirectSearchResults.FontSize = 35.0;
                        txtNoDirectSearchResults.Margin = new Thickness(0, 25, 0, 0);
                        txtNoDirectSearchResults.TextWrapping = TextWrapping.Wrap;
                        #endregion

                        RowDefinition newGridRow_NoDirectResultsText = new RowDefinition();
                        newGridRow_NoDirectResultsText.Height = new GridLength(0, GridUnitType.Auto);
                        gridContent.RowDefinitions.Add(newGridRow_NoDirectResultsText);

                        #region add the text block to the content stack panel
                        gridContent.Children.Add(txtNoDirectSearchResults);
                        Grid.SetRow(txtNoDirectSearchResults, rowCount);
                        Grid.SetColumn(txtNoDirectSearchResults, 0);
                        Grid.SetColumnSpan(txtNoDirectSearchResults, 2);

                        // increase the row offset since the maintain the correct index for the term controls
                        rowOffset += 1;
                        #endregion
                    }

                    // check if similar terms are available and show them
                    if (searchResponse.similarterms != null && searchResponse.similarterms.Count > 0)
                    {
                        //#region create a separator and add it to the content panel

                        #region Create a text block to inform the user about available similar results
                        TextBlock txtSimilarTerms = new TextBlock();
                        txtSimilarTerms.Text = AppResources.MessageSimilarResults;
                        txtSimilarTerms.TextAlignment = TextAlignment.Center;
                        txtSimilarTerms.VerticalAlignment = System.Windows.VerticalAlignment.Top;
                        txtSimilarTerms.FontSize = 35.0;
                        txtSimilarTerms.Margin = new Thickness(0, 50, 0, 25);
                        txtSimilarTerms.TextWrapping = TextWrapping.Wrap;
                        #endregion

                        #region add the text block to the content stack panel
                        RowDefinition newGridRow_SimilarTermText = new RowDefinition();
                        newGridRow_SimilarTermText.Height = new GridLength(0, GridUnitType.Auto);
                        gridContent.RowDefinitions.Add(newGridRow_SimilarTermText);

                        gridContent.Children.Add(txtSimilarTerms);
                        Grid.SetRow(txtSimilarTerms, rowCount + rowOffset);
                        Grid.SetColumn(txtSimilarTerms, 0);
                        Grid.SetColumnSpan(txtSimilarTerms, 2);

                        // increase the row offset since the maintain the correct index for the term controls
                        rowOffset += 1;
                        #endregion

                        // Create new control which will show the alternate term
                        Button btnSimilarTerm = null;

                        // Create a new border control to enable rounded corners to the returned alternate terms
                        Border borderSimilarTerm = null;

                        // Iterate over all contained alternate terms
                        foreach (OpenThesaurus.DAOs.Similarterm similarTerm in searchResponse.similarterms)
                        {
                            #region Create & configure the border control for similar terms
                            // parent control which will contain the term button
                            borderSimilarTerm = new Border();
                            borderSimilarTerm.CornerRadius = new CornerRadius(25);
                            borderSimilarTerm.BorderBrush = borderBrush; // assign the Phone Accent Color to the term border
                            borderSimilarTerm.BorderThickness = new Thickness(3, 3, 3, 3);
                            borderSimilarTerm.Margin = new Thickness(0, 0, 0, 10); // margin between each single control in list
                            #endregion

                            #region Use a TextBlock control for text to support word wrap for long words (without it, long word are simply cut)
                            TextBlock txtSimilarTermContent = new TextBlock();
                            txtSimilarTermContent.Text = similarTerm.term;
                            txtSimilarTermContent.TextAlignment = TextAlignment.Center;
                            txtSimilarTermContent.TextWrapping = TextWrapping.Wrap;
                            txtSimilarTermContent.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                            txtSimilarTermContent.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                            #endregion

                            #region Create & configure one button for each single term
                            btnSimilarTerm = new Button();
                            btnSimilarTerm.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                            btnSimilarTerm.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                            btnSimilarTerm.BorderThickness = new Thickness(0, 0, 0, 0); // remove button border - will be replaced by the rounded border of the parent control
                            btnSimilarTerm.Click += new RoutedEventHandler(OnTermButtonClicked);
                            #endregion

                            // assign the TextBlock control to the term button
                            btnSimilarTerm.Content = txtSimilarTermContent;

                            // put the button into the border control (necessary to have rounded corners)
                            borderSimilarTerm.Child = btnSimilarTerm;

                            #region Align the border similar term control in the content grid
                            RowDefinition newGridRow_SimilarTerm = new RowDefinition();
                            newGridRow_SimilarTerm.Height = new GridLength(0, GridUnitType.Auto);
                            gridContent.RowDefinitions.Add(newGridRow_SimilarTerm);

                            gridContent.Children.Add(borderSimilarTerm);
                            borderSimilarTerm.SetValue(Grid.RowProperty, rowCount + rowOffset);
                            borderSimilarTerm.SetValue(Grid.ColumnProperty, 0);
                            borderSimilarTerm.SetValue(Grid.ColumnSpanProperty, 2);
                            #endregion

                            rowCount++;
                        }
                    } // end of check for similar results

                    // check if substring terms are available and show them
                    if (searchResponse.substringterms != null && searchResponse.substringterms.Count > 0)
                    {
                        #region Create a text block with the respective message to inform the user
                        TextBlock txtSubStringTerms = new TextBlock();
                        txtSubStringTerms.Text = AppResources.MessageSubStringResults;
                        txtSubStringTerms.TextAlignment = TextAlignment.Center;
                        txtSubStringTerms.VerticalAlignment = System.Windows.VerticalAlignment.Top;
                        txtSubStringTerms.FontSize = 35.0;
                        txtSubStringTerms.Margin = new Thickness(0, 50, 0, 25);
                        txtSubStringTerms.TextWrapping = TextWrapping.Wrap;
                        #endregion

                        #region add the text block to the content stack panel
                        RowDefinition newGridRow_SubStringText = new RowDefinition();
                        newGridRow_SubStringText.Height = new GridLength(0, GridUnitType.Auto);
                        gridContent.RowDefinitions.Add(newGridRow_SubStringText);

                        gridContent.Children.Add(txtSubStringTerms);
                        Grid.SetRow(txtSubStringTerms, rowCount + rowOffset);
                        Grid.SetColumn(txtSubStringTerms, 0);
                        Grid.SetColumnSpan(txtSubStringTerms, 2);

                        // increase the row offset since the maintain the correct index for the term controls
                        rowOffset += 1;
                        #endregion

                        // Create new control which will show the substring term
                        Button btnSubstringTerm = null;

                        // Create a new border control to enable rounded corners to the returned substring terms
                        Border borderSubstringTerm = null;

                        // Iterate over all contained substring terms
                        foreach (OpenThesaurus.DAOs.Substringterm substring in searchResponse.substringterms)
                        {
                            #region Create & configure the border control for substring terms
                            // parent control which will contain the term button
                            borderSubstringTerm = new Border();
                            borderSubstringTerm.CornerRadius = new CornerRadius(25);
                            borderSubstringTerm.BorderBrush = borderBrush; // assign the Phone Accent Color to the term border
                            borderSubstringTerm.BorderThickness = new Thickness(3, 3, 3, 3);
                            borderSubstringTerm.Margin = new Thickness(0, 0, 0, 10); // margin between each single control in list
                            #endregion

                            #region Use a TextBlock control for text to support word wrap for long words (without it, long word are simply cut)
                            TextBlock txtSubstringTermContent = new TextBlock();
                            txtSubstringTermContent.Text = substring.term;
                            txtSubstringTermContent.TextAlignment = TextAlignment.Center;
                            txtSubstringTermContent.TextWrapping = TextWrapping.Wrap;
                            txtSubstringTermContent.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                            txtSubstringTermContent.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                            #endregion

                            #region Create & configure one button for each single term
                            btnSubstringTerm = new Button();
                            btnSubstringTerm.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                            btnSubstringTerm.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
                            btnSubstringTerm.BorderThickness = new Thickness(0, 0, 0, 0); // remove button border - will be replaced by the rounded border of the parent control
                            btnSubstringTerm.Click += new RoutedEventHandler(OnTermButtonClicked);
                            #endregion

                            // assign the TextBlock control to the term button
                            btnSubstringTerm.Content = txtSubstringTermContent;

                            // put the button into the border control (necessary to have rounded corners)
                            borderSubstringTerm.Child = btnSubstringTerm;

                            #region Align the border similar term control in the content grid
                            RowDefinition newGridRow_SubStringTerm = new RowDefinition();
                            newGridRow_SubStringTerm.Height = new GridLength(0, GridUnitType.Auto);
                            gridContent.RowDefinitions.Add(newGridRow_SubStringTerm);

                            gridContent.Children.Add(borderSubstringTerm);
                            borderSubstringTerm.SetValue(Grid.RowProperty, rowCount + rowOffset);
                            borderSubstringTerm.SetValue(Grid.ColumnProperty, 0);
                            borderSubstringTerm.SetValue(Grid.ColumnSpanProperty, 2);
                            #endregion

                            rowCount++;
                        }

                    } // end check for substring results

                    // add the last successful (=request was OK) search term to the search term history if not already listed in there.
                    if (!searchTermHistory.Contains(txtSearchInput.Text))
                    {
                        searchTermHistory.Add(txtSearchInput.Text);
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.WriteLine("Error while combining data and layout! in row: " + rowCount + " ==> " + ex.Message);
                    MessageBox.Show(AppResources.MessageInternalError, AppResources.MessageBoxTitleInfo, MessageBoxButton.OK);
                }
            }
            else
            {
                MessageBox.Show(AppResources.MessageNetworkError, AppResources.MessageBoxTitleInfo, MessageBoxButton.OK);
                Debug.WriteLine(e.Error.ToString());
            }

            // re-enable the submit button which was disabled during in DownloadProgressChanged()
            enableSubmitButton();

            // move focus from input field to the scroll viewer to hide the keyboard
            scrollViewerContent.Focus();
        }

        /// <summary>
        /// Starts text-to-speech synthesis of the current term.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void OnTtsButtonClicked(object sender, RoutedEventArgs e)
        {
            // tell the user that TTS isn't available, yet.
            if (!currentApp.TtsAvailable)
            {
                MessageBox.Show(AppResources.MessageNoTextToSpeechEngineAvailable, AppResources.MessageBoxTitleInfo, MessageBoxButton.OK);
            }
            else
            {
                // the tts button that was clicked as an object
                Button currentButton = sender as Button;

                // initializing the row count with an negative index
                int rowCount = -1;

                try
                {
                    // search for the separator character
                    int separatorPosition = currentButton.Name.IndexOf("_") + 1;

                    // get row count id from the current clicked button
                    rowCount = Int32.Parse(currentButton.Name.Substring(separatorPosition));
                    Debug.WriteLine("parsed row count from button name: " + rowCount);

                    // get the term border which contains the term button
                    // since the row count and the child id differs, the row count must be multiplied by two to match the term button.
                    Border termBorder = gridContent.Children[rowCount * 2] as Border;

                    // Get the term button from within the term border with the text which should be spoken
                    Button termButton = termBorder.Child as Button;

                    // Get the TextBlock control as Content of the term button
                    TextBlock txtTermText = termButton.Content as TextBlock;

                    // check if the term Button contains a valid string
                    //if (termButton.Content.ToString() != string.Empty)
                    if (txtTermText.Text != string.Empty)
                    {
                        Debug.WriteLine("Text to speak: " + txtTermText.Text);
                        // speak the text of the term button
                        await currentApp.textToSpeechSynthesizer.SpeakTextAsync(txtTermText.Text);
                    }
                    else
                    {
                        MessageBox.Show(AppResources.MessageInternalError, AppResources.MessageBoxTitleInfo, MessageBoxButton.OK);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Error while trying to determine and read the text-to-speak string!");
                    Debug.WriteLine(ex);
                }
            }
        }

        /// <summary>
        /// Event handler for the term button's OnClick method
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnTermButtonClicked(object sender, RoutedEventArgs e)
        {
            Button clickedButton = sender as Button;

            // get the TextBlock from the Content property
            TextBlock txtTermContent = clickedButton.Content as TextBlock;

            string cleanedSearchTerm = removeMetaDataFromSearchTerm(txtTermContent.Text);

            // apply the cleaned term to the search input field
            txtSearchInput.Text = cleanedSearchTerm;

            // get the new results for the clicked and cleaned term
            getTranslationFromBackend(cleanedSearchTerm);
        }

        /// <summary>
        /// Removes curved and square brackets from the search term.
        /// </summary>
        /// <param name="searchTermToClean">The search term to be cleaned</param>
        /// <returns>The plain search term.</returns>
        internal static string removeMetaDataFromSearchTerm(string searchTermToClean)
        {
            // Remove the optional additional context information from the term.
            // Otherwise most probably no search results will be found. Example for search term "Haus": "Hütte (ugs.)" ==> cleaned term "Hütte"
            int startCutIndex = searchTermToClean.IndexOf("(");

            // the cleaned search term without any additional information
            string cleanedSearchTerm = searchTermToClean;

            // only cut the text if the "(" character was found
            if (startCutIndex > 0)
            {
                Debug.WriteLine("Removing \"" + searchTermToClean.Substring(startCutIndex) + "\" from \"" + searchTermToClean + "\"");
                cleanedSearchTerm = searchTermToClean.Substring(0, startCutIndex);
            }

            // Remove the optional "level"-attribute from the term.
            // Otherwise most probably no search results would be found. Example for search term "Auto": "Schnittenschaukel [derb]" ==> cleaned term "Schnittenschaukel"
            startCutIndex = cleanedSearchTerm.IndexOf("[");

            // only cut the text if the "[" character was found
            if (startCutIndex > 0)
            {
                Debug.WriteLine("Removing \"" + cleanedSearchTerm.Substring(startCutIndex) + "\" from \"" + cleanedSearchTerm + "\"");
                cleanedSearchTerm = cleanedSearchTerm.Substring(0, startCutIndex);
            }

            // remove any optional leading or trailing characters or linebreaks
            cleanedSearchTerm = cleanedSearchTerm.Trim();
            return cleanedSearchTerm;
        }


        /// <summary>
        /// As soon as the submit button was tapped the translation process starts.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSubmit_Tap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            // query the API and process the response
            getTranslationFromBackend(txtSearchInput.Text);
        }

        /// <summary>
        /// Forwards the user to the rating page in the Windows Phone Marketplace.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void appBarRating_Click(object sender, System.EventArgs e)
        {
            MarketplaceReviewTask marketplaceReviewTask = new MarketplaceReviewTask();
            marketplaceReviewTask.Show();
        }

        /// <summary>
        /// Navigate to the info page.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void appBarInfo_Click(object sender, System.EventArgs e)
        {
            NavigationService.Navigate(new Uri("/Info.xaml", UriKind.Relative));
        }

        /// <summary>
        /// Sets the focus directly. Usability feature.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSearchInput_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            txtSearchInput.Focus();
        }

        /// <summary>
        /// Reacts on the ENTER-Key on the on-screen keyboard. If the ENTER key is pressed tapping on the submit button isn't necessary anymore. Usability feature.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSearchInput_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Enter)
            {
                updateSubmitButtonStatus();

                // only start a query 
                if (txtSearchInput.Text.Length > 1)
                {
                    // query the API and process the response
                    getTranslationFromBackend(txtSearchInput.Text);
                }
            }
        }

        /// <summary>
        /// Opens the history page.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void appBarHistory_Click(object sender, System.EventArgs e)
        {
            NavigationService.Navigate(new Uri("/History.xaml", UriKind.Relative));
        }

        /// <summary>
        /// Opens the options page.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void appBarOptions_Click(object sender, System.EventArgs e)
        {
            NavigationService.Navigate(new Uri("/Options.xaml", UriKind.Relative));
        }

        /// <summary>
        /// Opens the integrated browser and navigates to the contact form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void appBarContactForm_Click(object sender, System.EventArgs e)
        {
            WebBrowserTask webBrowserTask = new WebBrowserTask();
            webBrowserTask.Uri = new Uri("http://openthesaurus.azurewebsites.net/Kontaktformular.asp", UriKind.Absolute);
            webBrowserTask.Show();
        }

        /// <summary>
        /// Starts the speech to text dialog and submits the result automatically.
        /// The leading full stop will be removed automatically.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnStt_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            try
            {
                // Check if the SpeechRecognizerUI was initialized in App.xaml.cs. If not most probably the speech-to-text engine doesn't support the language, yet.
                if (currentApp.SttAvailable && currentApp.speechToTextControl != null)
                {
                    // throws System.NullReferenceException if no network is available
                    SpeechRecognitionUIResult result = await currentApp.speechToTextControl.RecognizeWithUIAsync();

                    // receive the recognized text and remove the trailing full stop
                    string recognizedText = result.RecognitionResult.Text;
                    recognizedText = recognizedText.Replace(".", "");

                    Debug.WriteLine("Recognized text: " + recognizedText);

                    // put the cleaned recognized text into the text input field
                    txtSearchInput.Text = recognizedText;

                    // automatically submit the recognized search term
                    getTranslationFromBackend(recognizedText);
                }
                else
                {
                    MessageBox.Show(AppResources.MessageNoSpeechToTextEngineAvailable, AppResources.MessageBoxTitleInfo, MessageBoxButton.OK);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("No network available. Execution of speech to text service not possible!\n" + ex.Message);
            }
        }

        /// <summary>
        /// Using a double tap the input text as well as the results gets deleted. Usability feature.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSearchInput_DoubleTap(object sender, System.Windows.Input.GestureEventArgs e)
        {
            txtSearchInput.Text = "";

            updateSubmitButtonStatus();

            // remove all existing child controls
            gridContent.Children.Clear();

            showHint();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PhoneApplicationPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            // Load the selected options after the page is loaded
            searchOptions.initializeOptionsFromStorage();

            if (currentApp.SttAvailable)
            {
                prepareStt();
            }
            
            showHint();
        }

        /// <summary>
        /// Changes the white Speech to text icon to a black one if the light background is active.
        /// </summary>
        private void showWhiteOrDarkSttIcon()
        {
            // check if the light theme is active
            if (Search.lightThemeActive)
            {
                // read a bitmap with the icon source
                BitmapImage imageSttSource = new BitmapImage(new Uri(@"Assets/microphoneBlack.png", UriKind.RelativeOrAbsolute));

                // set the black microphone icon. The white one would be invisible on white background.
                imageStt.Source = imageSttSource;
            }

        }

        /// <summary>
        /// Show a hint that the input can be deleted by tapping twice on the text input field when the content panel is empty.
        /// </summary>
        private void showHint()
        {
            // only show the hint if the content grid is empty (maybe old results are shown when coming back from the options page)
            if (gridContent.Children.Count < 1)
            {
                // Create a new instance of the custom user control for textual hints
                TextualHint hint = new TextualHint();

                // add a new grid row
                RowDefinition newGridRow = new RowDefinition();
                newGridRow.Height = new GridLength(0, GridUnitType.Auto);
                gridContent.RowDefinitions.Add(newGridRow);

                // add it to the grid control
                gridContent.Children.Add(hint);

                // Let the hint use all of the available space in the row
                Grid.SetRow(hint, 0);
                Grid.SetColumn(hint, 0);
                Grid.SetColumnSpan(hint, 2);
            }
        }

    }
}